﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;

namespace Game
{
    internal class AdjustPositionTool
    {
        public static void AdjustPosition(Vector3 tipPositionWS,
            float verticalOffset,
            HorizontalLayoutGroup max,
            RectTransform boundsRect,
            RectTransform contentBg)
        {
            max.enabled = true;
            LayoutRebuilder.ForceRebuildLayoutImmediate(max.GetComponent<RectTransform>());

            Vector3 tipLocalPosition = boundsRect.worldToLocalMatrix.MultiplyPoint3x4(tipPositionWS);
            var contentBounds = GetBounds(contentBg, boundsRect);

            var contentPos = tipLocalPosition;

            // 优先判断上方空间是否足够
            var top = tipLocalPosition.y + verticalOffset + contentBounds.size.y;
            var offset = verticalOffset + contentBounds.extents.y;

            var viewBounds = new Bounds(boundsRect.rect.center, boundsRect.rect.size);

            if (top >= viewBounds.max.y)
                offset = -offset;

            contentPos.y += offset;

            AdjuestPosition(ref contentPos, contentBounds, viewBounds);
            max.enabled = false;
            contentBg.transform.localPosition = contentPos;
        }

        private static void AdjuestPosition(ref Vector3 centerPos, Bounds bound, Bounds viewBounds)
        {
            bound.center = centerPos;

            var left = bound.min.x - viewBounds.min.x;
            if (left < 0)
                centerPos.x = bound.center.x - left;

            var right = bound.max.x - viewBounds.max.x;
            if (right > 0)
                centerPos.x = bound.center.x - right;

            var top = bound.max.y - viewBounds.max.y;
            if (top > 0)
                centerPos.y = bound.center.y - top;

            var bottom = bound.min.y - viewBounds.min.y;
            if (bottom < 0)
                centerPos.y = bound.center.y + bottom;
        }

        private static Bounds GetBounds(RectTransform rect, RectTransform boundsRect)
        {
            if (rect == null)
                return default(Bounds);

            Vector3[] m_Corners = new Vector3[4];
            rect.GetWorldCorners(m_Corners);
            Matrix4x4 viewWorldToLocalMatrix = boundsRect.worldToLocalMatrix;
            return InternalGetBounds(m_Corners, ref viewWorldToLocalMatrix);
        }

        private static Bounds InternalGetBounds(Vector3[] corners, ref Matrix4x4 viewWorldToLocalMatrix)
        {
            Vector3 vector = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
            Vector3 vector2 = new Vector3(float.MinValue, float.MinValue, float.MinValue);
            for (int i = 0; i < 4; i++)
            {
                Vector3 lhs = viewWorldToLocalMatrix.MultiplyPoint3x4(corners[i]);
                vector = Vector3.Min(lhs, vector);
                vector2 = Vector3.Max(lhs, vector2);
            }

            Bounds result = new Bounds(vector, Vector3.zero);
            result.Encapsulate(vector2);
            return result;
        }
    }
}
